<?php

namespace Tests;

use Illuminate\Container\Container;
use Illuminate\Database\Capsule\Manager;
use Illuminate\Database\Connection;
use Illuminate\Database\Connectors\ConnectionFactory;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Fluent;
use Tests\Database\Migration\Table;
use Tests\Database\Seeder\Seeder;

abstract class TestCase extends \PHPUnit\Framework\TestCase
{
	protected $database = [];
	protected $tables;
	protected $seeders;

	public function __construct($name = null, array $data = [], $dataName = '')
	{
		parent::__construct($name, $data, $dataName);

		$this->tables = collect();
		$this->seeders = collect();

		Connection::resolverFor('sqlite', function ($connection, $database, $prefix, $config) {
			return new SQLiteConnection($connection, $database, $prefix, $config);
		});

		$dbConfig = [
			'default' => [
				'driver' => 'sqlite',
				'database' => $this->getSqlite(),
				'host' => 'localhost',
				'username' => 'root',
				'password' => '123456',
				'charset' => 'utf8',
				'collation' => 'utf8_unicode_ci',
				'prefix' => 'ims_',
				'port' => '3306',
				'strict' => false
			],
		];

		if (!empty($this->database)) {
			foreach ($this->database as $name => $config) {
				if (!is_array($config)) {
					continue;
				}
				$dbConfig[$name] = $config;
			}
		}

		$container = new Container();
		$container->instance('config', new Fluent());
		$container['config']['database.default'] = 'default';
		$container['config']['database.connections'] = $dbConfig;

		$factory = new ConnectionFactory($container);
		$dbManager = new DatabaseManager($container, $factory);

		$manager = new Manager($container);
		$manager->setAsGlobal();

		Model::setConnectionResolver($dbManager);
		\Tests\Model\Model::setDefaultConnection('default');

		$container['db'] = $dbManager;
		Facade::setFacadeApplication($container);
	}

	/**
	 * @return string
	 */
	protected function getSqlite()
	{
		return __DIR__ . '/Database/Cache/test.db';
	}

	public function setUp(): void /* The :void return type declaration that should be here would cause a BC issue */
	{
		parent::setUp();

		$sqlite = $this->getSqlite();
		if (!is_dir(dirname($sqlite))) {
			mkdir(dirname($sqlite));
		}
		if (!file_exists($sqlite)) {
			touch($sqlite);
		}

		Schema::connection('default')->dropAllTables();
	}

	protected function tearDown(): void
	{
		parent::tearDown();

		$sqlite = $this->getSqlite();
		if (file_exists($sqlite)) {
			unlink($sqlite);
		}
	}

	/**
	 * @param mixed ...$tables
	 * @return TestCase
	 */
	protected function initTables(...$tables)
	{
		collect($tables)->each(function (string $clazz) {
			$table = new $clazz;
			if ($table instanceof Table) {
				$table->down()->up();
			}
		});

		return $this;
	}

	/**
	 * @param mixed ...$seeders
	 * @return TestCase
	 */
	protected function initTableData(...$seeders)
	{
		collect($seeders)->each(function (string $clazz) {
			$seeder = new $clazz;
			if ($seeder instanceof Seeder) {
				$seeder->run();
			}
		});

		return $this;
	}

	/**
	 * 初始化数据库
	 * @param array $table
	 * @return TestCase
	 */
	protected function registerTables(...$table)
	{
		$this->tables = $this->tables->merge(collect(func_get_args()));

		return $this;
	}

	/**
	 * 初始化数据库
	 * @return $this
	 */
	protected function createTables()
	{
		$this->tables->each(function (Table $migration) {
			$migration->up();
		});
		return $this;
	}

	/**
	 * 初始化数据库
	 * @param mixed $seeder
	 * @return $this
	 */
	protected function registerSeeders(...$seeder)
	{
		$this->seeders = $this->seeders->merge(collect(func_get_args()));

		return $this;
	}

	protected function runSeeders()
	{
		$this->seeders->each(function (Seeder $seeder) {
			$seeder->run();
		});
		return $this;
	}

	/**
	 * 清理数据库
	 * @return $this
	 */
	protected function dropTables()
	{
		$this->tables->each(function (Table $migration) {
			$migration->down();
		});
		return $this;
	}
}
